#ifndef     __ENCODE_H
#define     __ENCODE_H

#include <stdint.h>
#include "./types.h"
#include <string.h>
#include <utility>

namespace utils {

template <typename T>
inline 
std::enable_if_t<IS_INT<T> && (sizeof (T) == 1), void>
encode(uint8_t *buf, T val) {
    buf[0] = val;
}

template <typename T>
inline 
std::enable_if_t<IS_INT<T> && (sizeof (T) == 2), void>
encode(uint8_t *buf, T val) {
    buf[0] = val >> 8;
    buf[1] = val;
}

template <typename T>
inline 
std::enable_if_t<IS_INT<T> && (sizeof (T) == 4), void>
encode(uint8_t *buf, T val) {
    buf[0] = val >> 24;
    buf[1] = val >> 16;
    buf[2] = val >> 8;
    buf[3] = val;
}

template <typename T>
inline 
std::enable_if_t<IS_ERROR<T>, void>
encode(uint8_t *buf, T err) {
    buf[0] = err.val;
}

template <typename T>
inline uint8_t encode_dyn(uint8_t *buf, const T& val) {
    buf[0] = val.len;
    memcpy(buf + 1, val.buf, val.len);
    return val.len + 1;
}

inline uint8_t encode_all(uint8_t *) {
    return 0;
}

template <typename T, typename... Ts>
inline uint8_t encode_all(uint8_t *buf, T&& val, Ts&&... ts) {
    using TYPE = std::decay_t<T>;
    static_assert(IS_CODEC_TYPE<TYPE>, "encode type fail");

    if constexpr (IS_INT<TYPE> || IS_ERROR<TYPE>) {
        constexpr uint8_t len = sizeof (TYPE);
        encode(buf, val);
        if constexpr (sizeof...(Ts) != 0) {
            return len + encode_all(buf + len, std::forward<Ts>(ts)...);
        } else {
            return len;
        }
    } else if constexpr (IS_BYTE_VIEW<TYPE>) {
        uint8_t len = encode_dyn(buf, val);
        if constexpr (sizeof...(Ts) != 0) {
            return len + encode_all(buf + len, std::forward<Ts>(ts)...);
        } else {
            return len;
        }
    } else {
        return 0;
    }
}

}

#endif
